#include "UIManager.h"

namespace Lame3d
{
	UIManager::UIManager()
			: draggedWindow(NULL), pressedElement(NULL), activeTextEntry(NULL), dragDown(false), 
			resizeXPositive(false), resizeYPositive(false), resizeDown(false),
			repeatEvent(NULL), keyRepeatDelay(.25), delayCounter(0), keyRepeatSeconds(.1), repeatCounter(0)
	{
		fadeTimer.Seconds();
	}
	
	UIManager::~UIManager()
	{
		std::list<Window*>::iterator it;
		for(it = windowList.begin(); it != windowList.end(); ++it) {
			delete *it;
		}
		std::list<UICallback*>::iterator it2;
		for(it2 = callbacks.begin(); it2 != callbacks.end(); ++it2) {
			delete *it2;
		}
	}
	
	void UIManager::AddWindow(Window *w)
	{
		windowList.push_back(w);
		newWindows.push_back(w);
	}
	
	void UIManager::RemoveWindow(Window *w)
	{
		fadingWindows.push_back(w);
	}
	
	void UIManager::RegisterCallback(UICallback *c)
	{
		callbacks.push_back(c);
	}
	
	void UIManager::UnregisterCallback(UICallback *c)
	{
		callbacks.remove(c);
		delete c;
	}
	
	void UIManager::UnregisterCallback(const std::string &s)
	{
		std::list<UICallback*>::iterator it;
		for(it = callbacks.begin(); it != callbacks.end(); ) {
			if( (*it)->Name() == s ) {
				delete *it;
				it = callbacks.erase(it); 
			} else {
				++it;
			}
		}
	}
	
	void UIManager::Handle(const MouseMotionEvent &evt)
	{
		fadeWindows();
		
		float keyTime = keyRepeatTimer.Seconds();
		if( repeatEvent && 
				(delayCounter >= keyRepeatDelay) &&
				(repeatCounter += keyTime) >= keyRepeatSeconds) {
			repeatCounter = 0;
			Handle(*repeatEvent);
		} else if(repeatEvent) {
			delayCounter += keyTime; 
		}
		
		if(draggedWindow) {
			Point2d motion(evt.Motion());
			if(evt.EdgeX()) {
				motion[0] = 0;
			}
			if(evt.EdgeY()) {
				motion[1] = 0;
			};
			if(dragDown) {
				draggedWindow->Location() += motion;
			} else if(resizeDown && draggedWindow->Resizable()) {
				Point2d nSize = draggedWindow->Size();
				if(resizeXPositive) {
					nSize[0] += motion[0];
				} else {
					draggedWindow->Location()[0] += motion[0];
					nSize[0] -= motion[0];
				}
				if(resizeYPositive) {
					nSize[1] -= motion[1];
				} else {
					draggedWindow->Location()[1] += motion[1];
					nSize[1] += motion[1];
				}
				if(draggedWindow->Size()[0] < .15) {
					nSize[0] = .15;
				}
				if(draggedWindow->Size()[1] < .15) {
					nSize[1] = .15;
				}
				draggedWindow->Size(nSize);
			}
		} else {
		}
	}
	
	bool UIManager::Handle(const MouseClickEvent &evt)
	{
		if(evt.MiddleButton()) {
			dragDown = evt.Pressed();
			if(dragDown) {
				draggedWindow = selectWindow(evt.Location());
				if(draggedWindow) {
					raiseWindow(draggedWindow);
				}
			}
			return true;
		} else if(evt.RightButton()) {
			resizeDown = evt.Pressed();
			if(resizeDown) {
				draggedWindow = selectWindow(evt.Location());
				if(draggedWindow) {
					Point2d midP = draggedWindow->Location();
					midP[0] += draggedWindow->Size()[0]/2.0;
					midP[1] -= draggedWindow->Size()[1]/2.0;
					midP -= evt.Location();
					resizeXPositive = midP[0] < 0;
					resizeYPositive = midP[1] > 0;
					
					raiseWindow(draggedWindow);
				}
			}
			return true;
		} else if(evt.LeftButton()) {
			Window *w = selectWindow(evt.Location());
			if(w) {
				raiseWindow(w);
			}
			UIElement *e = selectElement(evt.Location());
			
			if(e) {
				if(evt.Pressed()) {
					pressedElement = e;
					UIButton *b = dynamic_cast<UIButton*>(e);
					if(b) {
						b->State(UIButton::DOWN);
					}				
				} else {
					// released
					UIButton *b = dynamic_cast<UIButton*>(pressedElement);
					if(b) {
						b->State(UIButton::UP);
					}
					if(e == pressedElement) {
						// released on element
						emit(*w, *pressedElement, pressedElement->ClickedMessage());
						pressedElement = NULL;
						
						// select UITextEntry if clicked!
						UITextEntry *tE = dynamic_cast<UITextEntry*>(e);
						if(tE) {
							activeTextEntry = tE;
							tE->HasFocus(true);
						}	
					} else {
						// released off element
						pressedElement = NULL;
					}
				}
			} else {
				// nothing selected
				if(pressedElement) {
					UIButton *b = dynamic_cast<UIButton*>(pressedElement);
					if(b) {
						b->State(UIButton::UP);
					}
				}
				pressedElement = NULL;
				// deselect selected text entry
				if(activeTextEntry) {
					activeTextEntry->HasFocus(false);
					activeTextEntry = NULL;
				}
			}
		}
		
		return false;
	}
	
	bool UIManager::Handle(const KeyEvent &evt)
	{
		if(activeTextEntry && evt.Pressed()) {
			activeTextEntry->Handle(evt);
			if(evt.Key() == SDLK_RETURN) {
				emit(*windowList.back(), *activeTextEntry, activeTextEntry->EnterMessage());
			}
			if(!activeTextEntry->HasFocus()) {
				// check for defocusing events.
				activeTextEntry = NULL;
			}
			
			repeatEvent = new KeyEvent(evt);
			keyRepeatTimer.Seconds();
			repeatCounter = 0;
			delayCounter = 0;
			
			return true;
		} else if(!evt.Pressed()) {
			if(repeatEvent) {
				delete repeatEvent;
			}
			repeatEvent = NULL;
		}
		return false;
	}
	
	std::list<Window*>::const_iterator UIManager::WindowBegin() const
	{
		return windowList.begin();
	}
	
	std::list<Window*>::const_iterator UIManager::WindowEnd() const
	{
		return windowList.end();
	}
	
	float UIManager::KeyRepeatSeconds() const
	{
		return keyRepeatSeconds;
	}
	
	void UIManager::KeyRepeatSeconds(float f)
	{
		keyRepeatSeconds = f;
	}
	
	float UIManager::KeyRepeatDelay() const
	{
		return keyRepeatDelay;
	}
	
	void UIManager::KeyRepeatDelay(float f)
	{
		keyRepeatDelay = f;
	}
	
	Window* UIManager::selectWindow(const Point2d &pt) const
	{
		//printf("Checking for selection at %f, %f...\n", pt[0], pt[1]);
		std::list<Window*>::const_reverse_iterator it;
		Window *sel = NULL;
		for(it = windowList.rbegin(); it != windowList.rend(); ++it) {
			if((*it)->Contains(pt)) {
				sel = *it;
				break;
			}
		}
		if(sel) {
			printf("Selected window: %s\n", sel->Name().c_str());
		}
		return sel;
	}
	
	UIElement* UIManager::selectElement(const Point2d &pt) const
	{
		Window *w = selectWindow(pt);
		if(w) {
			return w->ElementAt(pt);
		}
		return NULL;
	}
	
	void UIManager::raiseWindow(Window *w) 
	{
		windowList.remove(w);
		windowList.push_back(w);
	}
	
	void UIManager::fadeWindows()
	{
		// time for a window to fade, in seconds
		#define FADE_TIME .15
		#define INV_FADE_TIME 1.0/FADE_TIME
		
		float fDiff = fadeTimer.Seconds() * INV_FADE_TIME;
		// fade in windows
		std::list<Window*>::iterator it;
		for(it = newWindows.begin(); it != newWindows.end(); ) {
			Window *w = *it;
			float op = w->Opacity() + fDiff;
			if(op > 1.0) {
				op = 1.0;
				w->Opacity(op);
				it = newWindows.erase(it);
			} else {
				w->Opacity(op);
				++it;
			}
		}
		// fade out windows
		for(it = fadingWindows.begin(); it != fadingWindows.end(); ) {
			Window *w = *it;
			float op = w->Opacity() - fDiff;
			if(op < 0.0) {
				// delete
				windowList.remove(w);
				it = fadingWindows.erase(it);
				delete w;
			} else {
				w->Opacity(op);
				++it;
			}
		}
		
		#undef FADE_TIME
		#undef INV_FADE_TIME
	}
	
	void UIManager::emit(Window &w, UIElement &e, const std::string &message)
	{
		printf("Emitted: \"%s\" from window \"%s\" and element \"%s\"...\n", message.c_str(), w.Name().c_str(), e.Name().c_str());
		std::list<UICallback*>::iterator it;
		for(it = callbacks.begin(); it != callbacks.end(); ++it) {
			(*it)->Handle(w, e, message);
		}
	}
}

// eof //
